#!/bin/awk -f

# Usage:
#     awk -f dtcc.awk

#  Author:     paul_m_thompson@yahoo.com
#  File:       C:\Users\Public\Documents\Virtual Machines\VmWareShare\Source\awk\DTCC\dtcc.awk
#  Script:     DTCC.awk
#  Revision:   $Revision: 229 $
#  Id:         $Id: dtcc.awk 229 2014-11-19 20:13:28Z mnemotronic $

# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------


# Return 1 if file exists AND is readable AND isn't blank, 0 otherwise.
# Call with one param :: the filename to check.  The second param becomes a
#  local variable so that NF isn't modified.
function exists(fname , line) {
   if ( (getline line < fname) > 0 ) {
      close(fname) ;
      return 1 ;
   }
   else {
      return 0 ;
   }
}

function windows(is_windows) {
   is_windows = 0;
   if (index(tolower(ENVIRON["OS"]), "windows") > 0) {
      is_windows = 1;
   }
   return is_windows ;
}

# Return the system level / command prompt param quoted for the current OS.
# Assume Windows uses DOUBLEQUOTE and *nix uses QUOTE.
function quoted(str) {
   if ( windows() ) {
      return "\"" str "\"" ;
   }
   else {
      return "\'" str "\'" ;
   }
}

# POSIX awk does not support "length(array)"
# From: http://stackoverflow.com/a/19411381/1368561
function alen( a, i, k) {
   for( i in a) k++
   return k
}

# string param is passed by value.
# gsub returns number of substitutions made on 3rd param.
function trim( s ) {
   gsub(/^[\001-\037]+/ , "" , s) ;
   gsub(/[\001-\037]+$/ , "", s) ;
   return s ;
}


BEGIN {
   print "\n------------------------------------------------------------- BEGIN\n" ;

   # Internally, both Unix and Windows paths need to use SLASH delimiter.
   # Use BACKSLASH delimiter for Windows paths passed to Windows.
   # Use SLASH delimiter for Windows paths passed to *nix (i.e. GnuWin)
   #     utils run in Windows.

   DEBUG=1;
   # Record separator is one or more of BACKSPACE, FORMFEED, CARRIAGE RETURN, LINEFEED.
   RS = "[\b\f\n\r]+" ;

   # Unix paths will use SLASH.
   if ( windows() ) {
      # In Windows, filenames are not case sensitive.
      print "*** DEBUG ***  This is a Windows machine." ;
      IGNORECASE = 1 ;
      PATHDELIM = "\\" ;
      WORKING_DIR    = "C:\\Users\\Public\\Documents\\Virtual Machines\\VmWareShare\\Source\\awk" ;
      DEPLOY_DIR     = "C:\\Users\\Public\\Documents\\Virtual Machines\\VmWareShare\\Source\\awk\\deploy";
      MANIFEST_FNAME = "C:\\Users\\Public\\Documents\\Virtual Machines\\VmWareShare\\Source\\awk\\manifest.txt" ;
      PROCESS_FNAME  = "C:\\Users\\Public\\Documents\\Virtual Machines\\VmWareShare\\Source\\awk\\process.txt" ;

   }
   else {
      print "*** DEBUG ***  This is NOT a Windows machine." ;
      PATHDELIM = "/" ;
##      WORKING_DIR    = "/opt/serena/Release_Automation_Agent/core/var/work/EPN_Oracle" ;
      WORKING_DIR    = "/opt/serena/Release_Automation_Agent/core/var/work/HDF_ORACLE_HDFQ" ;
      DEPLOY_DIR     = "/opt/serena/Release_Automation_Agent/core/var/work/tmp/deploy";
##      MANIFEST_FNAME = "/opt/serena/Release_Automation_Agent/core/var/work/EPN_Oracle/manifest.txt" ;
      MANIFEST_FNAME = "/opt/serena/Release_Automation_Agent/core/var/work/HDF_ORACLE_HDFQ/manifest.txt" ;

      PROCESS_FNAME  = "/opt/serena/Release_Automation_Agent/core/var/work/tmp/process.txt" ;

      # New requirement 14-Oct-2014.
      PREDEPLOY_FNAME  = "/opt/serena/Release_Automation_Agent/core/var/work/tmp/predeploy_loop.txt" ;
      POSTDEPLOY_FNAME  = "/opt/serena/Release_Automation_Agent/core/var/work/tmp/postdeploy_loop.txt" ;
   }


   # Startup diagnostics

   # Redirect STDIN to the process.txt file.
   printf("\nRedirecty STDIN to process.txt file '%s'\n",PROCESS_FNAME);
   ARGV[1] = PROCESS_FNAME;
   if (ARGC < 2)
      ++ARGC ;

   if (DEBUG)
      printf("\nDEBUG:\n   FILENAME='%s'\n   FNR=%d\n   ARGC=%d\n   ARGV[0]='%s'\n   ARGV[1]='%s'\n\n\n",FILENAME,FNR,ARGC,ARGV[0],ARGV[1]);


   # Manifest file missing.  How big a deal is it???
   if (! exists(MANIFEST_FNAME)) {
      printf("Manifest file '%s' not found.\n",MANIFEST_FNAME);
      # Exit?  Keep on truckin?
      # exit 1 ;
   }


   # Process.txt file missing seems like a pretty tragic error.  What do we do?
   if (! exists(PROCESS_FNAME)) {
      printf("Process.txt file '%s' not found.\n",PROCESS_FNAME);
      # Probably don't want to go any further?
      # exit 1 ;
   }

   # New requirement 14-Oct-2014:  If Oracle, prepend and append special files
   #  to the artifact file.  We know it's Oracle if the "prepend" and "append"
   #  files exist.
   # Bail out with error if only one of the files exists.
   if ( (exists(PREDEPLOY_FNAME) && !exists(POSTDEPLOY_FNAME))  ||  (!exists(PREDEPLOY_FNAME) && exists(POSTDEPLOY_FNAME)) ) {
      printf("ERROR: Only one of the Pre / Post deploy files exists.  Exiting with error code.\n",PROCESS_FNAME);
      exit 1 ;
   }

   if (exists(PREDEPLOY_FNAME) && exists(POSTDEPLOY_FNAME)) {
      pre_post_files_available = 1 ;
      if (DEBUG)
         printf("\nDEBUG:\n Oracle deploy -- Add Pre & Post-Deploy scripts to artifact.\n");
   }
   else {
      pre_post_files_available = 0 ;
      if (DEBUG)
         printf("\nDEBUG:\n Pre & Post-Deploy scripts not found.\n");
   }


   # We will build a string containing all the filenames from the manifest
   #  file delimited by some character.  When scanning process.txt, this
   #  will allow us to quickly determine if the current line from process.txt
   #  is in the manifest file (using regular expression search).
   # The alternative would be to iterate the manifest array for each line
   #  in process.txt.  When manifest.txt or process.txt file gets large,
   #  that iteration could take a long time.
   manifest_filelist_delimiter = ";" ;
   delimited_manifest_filelist = "" ;

   # Flag that indicates we need to write the contents of the manifest
   #  array back to the manifest file.
   manifest_modified = 0;

   max_sequence_id = 0 ;

   # errors on an empty directory.
   if ( windows() ) {
      if (DEBUG)
         print "DEBUG: del /Q /S /F ",quoted(DEPLOY_DIR  PATHDELIM  "*") ;
      rslt = system("del /Q /S /F  "  quoted(DEPLOY_DIR  PATHDELIM  "*") ) ;
   }
   else {
      if (DEBUG)
         print "DEBUG: rm -rf ",quoted(DEPLOY_DIR  PATHDELIM  "*") ;
      rslt = system("rm -rf "  quoted(DEPLOY_DIR  PATHDELIM  "*") ) ;
   }

   printf("\n\nclean DEPLOY dir result=%d\n",rslt);


   # Read the manifest file and build associative array.
   # Array indicies are filenames, array values are the sequence number.
   # index                 value
   # --------------------- -----
   # file4.sql                11
   # file0021.sql              2
   # file13.txt               99
   # file123.sql               1


   while (getline<MANIFEST_FNAME > 0) {
      # Ignore comment and blank lines.
      # Should get 2 fields but WHITESPACE at end of line causes us to get NF=3
      if ((NF == 2 || NF == 3)  &&  $1 != "#") {
#      if (NF == 2  &&  $1 != "#") {
         # $1 is sequence number as a string, $2 is filename.
         fname = trim($2) ;
         # Index is sequence number as an integer.  Save max.
         idx = $1 + 0;
         if (idx > max_sequence_id)
            max_sequence_id = idx ;

         # Add filename to array.
         manifest[fname]=idx ;
         delimited_manifest_filelist = delimited_manifest_filelist  manifest_filelist_delimiter  fname ;
         if (DEBUG)
            printf("DEBUG: Line from manifest.txt: %s\n   Seq: %s (%d) ; fname=%s \n",$0,$1,idx,fname) ;
      } else {
         printf("DEBUG: not a number:name manifest file line\n") ;
         printf("       NF: %d\n",NF) ;
         printf("       line: '%s'\n",$0) ;
         printf("       seq: '%s'\n",$1) ;
         printf("       fname: '%s'\n",$2) ;
      }
   }

   # now a string like ";file1.sql;file2.sql;file3.sql;"
   delimited_manifest_filelist = delimited_manifest_filelist  manifest_filelist_delimiter ;

   if (DEBUG)
      printf("DEBUG:\n\nmax sequence id = %d ; alen=%d\ndelimited_manifest_list=%s\n",
         max_sequence_id,alen(manifest),delimited_manifest_filelist);

   if (DEBUG)
      for (idx in manifest) {
         printf("DEBUG: array: manifest[%s]=%d\n",idx,manifest[idx]);
      }

   # nawk -f Dtcc.awk process.txt
   #     ARGC=2   ARGV[0]=nawk  [1]=process.txt

   print "\n------------------------------------------------------------- pattern-action section\n" ;

}

# -----------------------------------------------------------------------------

# if current file from process.txt is in the manifest list it has been deployed.
delimited_manifest_filelist ~ (manifest_filelist_delimiter  trim($1)  manifest_filelist_delimiter) {
   fname = trim($1) ;
   printf("    manifest contains process.txt file '%s' \n",fname);
   # delete that entry from manifest array
   delete manifest[fname];
   # set flag to save manifest array back to file.
   manifest_modified++ ;
}

# -----------------------------------------------------------------------------

END {

   DEBUG=1;

   print "\n----------------------------------------------------- END SECTION\n" ;
   print("DEBUG :: manifest_modified="  manifest_modified);
   print("DEBUG :: max_sequence_id="  max_sequence_id);
   print("DEBUG :: alen(manifest)="  alen(manifest) );



   # Create a new manifest array swapping the index and value of all elements
   #  of the current manifest array.  This will yield an array where the
   #  indicies are the numeric sequence numbers and the values are the filenames.
   #  When this is iterated we get the file names in order by sequence number.
   for (idx in manifest) {
      sequenced_manifest[manifest[idx]] = idx ;
      if (DEBUG)
         printf("DEBUG: array: manifest[%s]=%d\n",idx,manifest[idx]);
   }
   print("DEBUG :: alen(sequenced_manifest)="  alen(sequenced_manifest) );

   # - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - --

   if (manifest_modified > 0) {

      if (DEBUG)
         print "DEBUG: # of manifest changes=",manifest_modified ;

      # Save updated manifest.  While doing this keep track of number of valid
      #  filename lines we write to the manifest file.  If it's none, then the
      #  manifest list is empty which means everything has been deployed which
      #  means this deploy cycle is complete!  Woo-Hoo!
      #  Delete the manifest file as a signal to RLM.

      if (DEBUG)
         print "DEBUG: Update manifest file on disk." ;

      manifest_entries = 0 ;
      for (i=0 ; i<=max_sequence_id ; i++) {

         if (DEBUG)
            printf("DEBUG: array: sequenced_manifest[%09d]=%s : strlen=%d\n",i,sequenced_manifest[i],length(sequenced_manifest[i]) );

         if (length(sequenced_manifest[i])) {
            printf("%09d %s\n",i,sequenced_manifest[i])>MANIFEST_FNAME ;
            ++manifest_entries ;
         }
      }

      if (manifest_entries < 1) {
         print("Manifest file is now empty.  All files deployed.  Deleting empty manifest file\n") ;
         rslt = system("rm -f " quoted(MANIFEST_FNAME) )  ;
         if (DEBUG)
            print ("DEBUG:: rm -f " quoted(MANIFEST_FNAME) " :: result="  rslt)  ;
         exit 0 ;
      }
   } else {
      if (DEBUG)
         print ("DEBUG:: Manifest not modified")  ;
   }

   # - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - --

   # For each line in manifest, if file exists in working dir then copy to
   #  deploy dir and quit script immediately.
   # If file doesn't exist in the working dir, then the working dir is out
   #  of sync with the manifest list.  Exit immediately with error status.
   for (i=0 ; i<=max_sequence_id ; i++) {

      if (length(sequenced_manifest[i])) {
         rslt = "" ;
         fname = WORKING_DIR  PATHDELIM  sequenced_manifest[i] ;
         dest_fname = DEPLOY_DIR PATHDELIM sequenced_manifest[i] ;

         if (exists(fname)) {
            print "At least 1 file remaining in manifest list.  Copying first file in list from working dir to deploy dir.\n" ;
            if ( windows() ) {
               printf("Windows copy : xcopy %s /F /R /Y /V \n" , quoted(fname) " " quoted(DEPLOY_DIR) ) ;
               rslt = system("xcopy  "  quoted(fname) " " quoted(DEPLOY_DIR) " /F /R /Y /V " ) ;
               print "xcopy result=",rslt ;
            }
            else {
               # other possibilities: "\\cp -rf " ; "yes | cp -rf "

               # New requirement 14-Oct-2014: Prepend and append files to artifact
               # pre_post_files_available = exists(PREDEPLOY_FNAME) && exists(POSTDEPLOY_FNAME) ;
               if (pre_post_files_available) {
                  # need to prepend // append
                  # cat PREDEPLOY_FNAME fname POSTDEPLOY_FNAME > dest_fname ;
                  printf("UNIX cat : /bin/cat  %s %s %s > %s \n" , quoted(PREDEPLOY_FNAME) , quoted(fname) , quoted(POSTDEPLOY_FNAME) , quoted(dest_fname) ) ;
                  rslt = system("/bin/cat "  quoted(PREDEPLOY_FNAME) " " quoted(fname) " " quoted(POSTDEPLOY_FNAME) " > " quoted(dest_fname) ) ;
                  print "cat result=",rslt ;
               }
               else {
                  printf("UNIX copy : /bin/cp -rf  %s \n" , quoted(fname) " " quoted(DEPLOY_DIR) ) ;
                  rslt = system("/bin/cp -rf "  quoted(fname) " " quoted(DEPLOY_DIR) ) ;
                  print "cp result=",rslt ;
               }


            }

            exit 0 ;
         }
         else {
            printf("ERROR!\nThe following file is in the manifest but was not found in the working dir:\n'%s'\n",quoted(WORKING_DIR  PATHDELIM  sequenced_manifest[i]) ) ;
            exit 1 ;
         }
      }
      else {
         printf("NOTE: length of sequenced_manifest[%d] is 0.\n",i);
      }
   }
}
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
